<?php

namespace App\Http\Middleware;

use App\Http\Controllers\Admin\IndexController;
use App\Models\SysAction;
use App\Models\SysGroup;
use App\Models\SysLog;
use App\Models\SysOperation;
use App\Models\SysToken;
use App\Utils\ErrorCode;
use App\Utils\ResponseTrait;
use Closure;
use Illuminate\Http\Request;

class AdminGuard
{
    use ResponseTrait;

    /**
     * token有效时间, 单位s
     */
    const TOKEN_TTL = 1800;

    const LOGIN_URL = '/login';

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $token = $request->header('Token');
        if (!$token) {
            return $this->loginRequired();
        }

        $tokenModel = SysToken::find($token);
        if (!$tokenModel || !$tokenModel['user'] || !$tokenModel['user']['enabled'] ||
            strtotime($tokenModel['updated_at']) + self::TOKEN_TTL < $_SERVER['REQUEST_TIME']) {
            return $this->loginRequired();
        }

        if (!$this->checkPrivilege($tokenModel['user']['group_id'], $request)) {
            return $this->error(...ErrorCode::INSUFFICIENT_PRIVILEGES);
        }

        $this->log($request, $tokenModel['user']['id']);

        $tokenModel->touch();
        $request->attributes->set('user', $tokenModel['user']);

        return $next($request);
    }

    private function checkPrivilege(int $groupId, Request $request): bool
    {
        $currentAction = $request->route()[1]['uses'];
        if (explode('@', $currentAction)[0] === IndexController::class) {
            return true;
        }

        $currentAction = str_replace('App\Http\Controllers\Admin\\', '', $currentAction);
        $actionModel = SysAction::query()->where('action', $currentAction)->first();
        if (!$actionModel) return false;

        $groupModel = SysGroup::query()->findOrFail($groupId);
        if (!$groupModel['operation_ids']) return false;
        $actions = SysOperation::query()
            ->whereIn('id', $groupModel['operation_ids'])
            ->selectRaw('GROUP_CONCAT(actions) actions')->value('actions');

        return in_array($actionModel->id, explode(',', $actions));
    }

    private function log(Request $request, int $userId)
    {
        if ($request->method() === 'GET') return;

        $action = $request->route()[1]['uses'];
        $action = str_replace('App\Http\Controllers\Admin\\', '', $action);

        $params = $request->all();
        empty($params['password']) or $params['password'] = '********';
        empty($params['new_password']) or $params['new_password'] = '********';
        $context = ['path' => $request->path(), 'params' => $params];
        SysLog::query()->forceCreate([
            'user_id' => $userId,
            'action' => $action,
            'context' => json_encode($context, JSON_UNESCAPED_UNICODE)
        ]);
    }

    private function loginRequired()
    {
        $errCode= ErrorCode::LOGIN_REQUIRED;
        return $this->error($errCode[0], $errCode[1], ['redirect' => self::LOGIN_URL]);
    }
}
